package com.search.api.service.impl;

import com.search.api.dto.FileDo;
import com.search.api.dto.SearchDo;
import com.search.api.dto.ReturnDo;
import com.search.api.service.ViewFileService;
import com.search.api.util.FileUtils;
import com.search.api.websocket.WebSocketServer;
import com.spire.doc.Document;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.documents.TextSelection;
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.general.find.PdfTextFind;
import com.spire.xls.CellRange;
import com.spire.xls.Workbook;
import com.spire.xls.Worksheet;
import com.spire.xls.collections.WorksheetsCollection;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashSet;


/**
 * @author huej
 * @description 进行关键字搜索 返回文件信息
 * @date 2021/7/14 11:01 下午
 */
@Service
public class ViewFileServiceImpl implements ViewFileService {
    private final WebSocketServer webSocketServer;

    /**
     * 构造注入方法
     * @param webSocketServer websocket 网络协议构造注入
     */
    public ViewFileServiceImpl(WebSocketServer webSocketServer) {
        this.webSocketServer = webSocketServer;
    }

    /**
     * 搜索关键字 返回文件信息，以及附加信息
     *
     * @param searchDO 前端输入搜索参数
     * @return 返回符合条件的文件信息
     */
    @Override
    public ReturnDo viewFile(SearchDo searchDO) throws Exception {
        long startTime = System.currentTimeMillis();   //获取开始时间
        ReturnDo returnDO = new ReturnDo();
        System.out.println("======================开始搜索========================");
        doGetFileList(searchDO.getFilePath(), searchDO, returnDO);
        System.out.println("======================结束搜索========================");
        long endTime = System.currentTimeMillis();//系统结束时间
        //设置系统耗费时间
        returnDO.setConsumeTime(String.valueOf((endTime - startTime) / 1000));
        return returnDO;
    }

    /**
     * 递归遍历文件list
     *
     * @param filePath 文件路径 每次递归需要去修改这个属性，如果直接用searchDo里面filepath 调用出后对下次递归有影响
     * @param searchDO 搜索的参数前端传递过来
     * @param returnDO 返回的文件list、以及其他附加信息
     */
    private void doGetFileList(String filePath, SearchDo searchDO, ReturnDo returnDO) throws Exception {
        //创建file文件
        File file = new File(filePath);
        if (file.exists()) {
            if (file.isDirectory()) {
                String[] fileNames = file.list();
                if (fileNames == null) {
                    return;
                }
                //循环递归
                for (String fileName : fileNames) {
                    doGetFileList(filePath + File.separator + fileName, searchDO, returnDO);
                }
            } else {
                if (returnDO.getCountTotalFile() > searchDO.getMaxFileCount()) {
                    throw new Exception("扫描文件数量过大");
                }
                //总搜索文件数量+1
                returnDO.setCountTotalFile(returnDO.getCountTotalFile() + 1);
                String fileName = file.getName();
                String fileType = FileUtils.getFileType(fileName);
                //按前端输入的文件类型进行过滤
                if (searchDO.getFilterFileTypes() == null || searchDO.getFilterFileTypes().contains(fileType)) {
                    //符合文件类型过滤条件的文件数量加一
                    returnDO.setCountFilterFileType(returnDO.getCountFilterFileType() + 1);
                    //System.out.println(spf.format(date) + " 文件搜索中(" + findFileCount + "|"+myFileInfo.getMayFindCount()+"):" + filePath);
                    if (searchDO.getKeyWords() == null || searchDO.getKeyWords().size() == 0) {
                        FileDo fileDo = new FileDo();
                        //设置文件名称
                        fileDo.setFileNameHtml(fileName);
                        fileDo.setFileName(fileName);
                        //设置文件路径
                        fileDo.setFilePath(file.getAbsolutePath());
                        //设置文件类型
                        fileDo.setFileType(fileType);
                        returnDO.getFileDos().add(fileDo);
                        returnDO.setCountKeyWordsFile(returnDO.getCountWordFile() + 1);
                    } else {
                        doFindFile(returnDO,searchDO, fileType, filePath);
                    }
                }
            }
        }
    }

    /**
     * 文件处理分发
     *
     * @param returnDo 总数数据 返回前端
     * @param searchDo webSocket需要使用
     * @param fileType 文件类型
     * @param filePath 文件路径
     * @throws IOException 文件处理异常
     */
    private void doFindFile(ReturnDo returnDo, SearchDo searchDo, String fileType, String filePath) throws IOException {
        long startTime = System.currentTimeMillis();   //获取开始时间
        if (".doc".equals(fileType) || ".docx".equals(fileType)) {
            doWord(returnDo, searchDo,filePath);
        } else if (".pdf".equals(fileType)) {
            doPdf(returnDo, searchDo,filePath);
        } else if (".xls".equals(fileType) || ".xlsx".equals(fileType)) {
            doExcel(returnDo, searchDo,filePath);
        } else {
            System.out.println("未处理文件:" + filePath);
        }
        long endTime = System.currentTimeMillis(); //获取结束时间
        String runTime =String.valueOf(endTime - startTime);
        System.out.println(" 文件搜索中(" + returnDo.getCountFilterFileType() + "|"+searchDo.getMayFindCount()+"):" + filePath);
        webSocketServer.sendMessage(searchDo.getUid(), (returnDo.getCountFilterFileType() * 100) / searchDo.getMayFindCount() + "##@@" + FileUtils.getFileName(filePath) + "**&&" + runTime);
    }

    /**
     * 处理word文件的搜索
     * @param returnDo 返回给前端的数据
     * @param searchDo 前端传过来搜索的过滤条件
     * @param filePath 文件路径
     */
    private void doWord(ReturnDo returnDo, SearchDo searchDo, String filePath) {
        try {
            Document document = new Document(filePath);
            for (String keyWord : searchDo.getKeyWords()) {
                TextSelection[] allString = document.findAllString(keyWord, true, true);
                if (!(null == allString)) {
                    FileDo fileDo = new FileDo();
                    fileDo.setFileName(FileUtils.getFileName(filePath));
                    //html的文件名称主要是excel高亮工作簿的时候用到
                    fileDo.setFileNameHtml(FileUtils.getFileName(filePath));
                    fileDo.setFilePath(filePath);
                    fileDo.setFileType(FileUtils.getFileType(filePath));
                    fileDo.setKeyWord(keyWord);
                    HashSet<String> content = new LinkedHashSet<>();
                    LinkedHashSet<String> contentHtml = new LinkedHashSet<>();
                    for (TextSelection textSelection : allString) {
                        Paragraph paragraph = (Paragraph) textSelection.getAsOneRange().getOwner();
                        content.add(paragraph.getText());
                        contentHtml.add(FileUtils.setKeyWordHeightLight(paragraph.getText(), keyWord, "danger"));
                    }
                    fileDo.setContent(content);
                    fileDo.setContentHtml(contentHtml);
                    returnDo.getFileDos().add(fileDo);
                    //符合关键字的文件数量加一
                    returnDo.setCountKeyWordsFile(returnDo.getCountKeyWordsFile()+1);
                    //word文件数量加一
                    returnDo.setCountWordFile(returnDo.getCountWordFile()+1);
                 }
            }
        } catch (Exception exception) {
            System.out.println("处理word时异常！" + exception.getMessage());
        }
    }


    /**
     * 处理pdf文件的搜索
     * @param returnDo 返回给前端的数据
     * @param searchDo 前端传过来搜索的过滤条件
     * @param filePath 文件路径
     */
    private void doPdf(ReturnDo returnDo, SearchDo searchDo, String filePath) {
        try {
            PdfDocument pdf = new PdfDocument(filePath);
            for (String keyWord : searchDo.getKeyWords()) {
                HashSet<String> content = new LinkedHashSet<>();
                boolean findKeyWord = false;
                LinkedHashSet<String> contentHtml = new LinkedHashSet<>();
                for (PdfPageBase page : (Iterable<PdfPageBase>) pdf.getPages()) {
                    PdfTextFind[] res = page.findText(keyWord).getFinds();
                    if (res.length > 0) {
                        findKeyWord = true;
                        for (PdfTextFind re : res) {
                            String lineText = re.getLineText();
                            content.add(lineText);
                            contentHtml.add(FileUtils.setKeyWordHeightLight(lineText, keyWord, "danger"));
                        }
                    }
                }
                if (findKeyWord) {
                    FileDo fileDo = new FileDo();
                    fileDo.setFileName(FileUtils.getFileName(filePath));
                    fileDo.setFilePath(filePath);
                    fileDo.setFileNameHtml(FileUtils.getFileName(filePath));
                    fileDo.setFileType(FileUtils.getFileType(filePath));
                    fileDo.setContentHtml(contentHtml);
                    fileDo.setKeyWord(keyWord);
                    fileDo.setContent(content);
                    returnDo.getFileDos().add(fileDo);
                    returnDo.setCountKeyWordsFile(returnDo.getCountKeyWordsFile()+1);
                    //搜索到的PDF文件数量加一
                    returnDo.setCountPdfFile(returnDo.getCountPdfFile()+1);
                }
            }
        } catch (Exception exception) {
            System.out.println("处理pdf时异常！" + exception.getMessage());
        }

    }

    /**
     * 处理Excel文件的搜索
     * @param returnDo 返回给前端的数据
     * @param searchDo 前端传过来搜索的过滤条件
     * @param filePath 文件路径
     */
    private void doExcel(ReturnDo returnDo, SearchDo searchDo, String filePath) {
        try {
            Workbook workbook = new Workbook();
            workbook.loadFromFile(filePath);
            //获取excel工作簿
            WorksheetsCollection worksheets = workbook.getWorksheets();
            //循环工作簿
            for (int i = 0; i < worksheets.size(); i++) {
                Worksheet worksheet = worksheets.get(i);
                //循环关键字
                for (String keyWord : searchDo.getKeyWords()) {
                    //循环获取得到关键字段
                    CellRange[] allString = worksheet.findAllString(keyWord, false, false);
                    if (allString.length > 0) {
                        //匹配到关键字
                        FileDo fileDo = new FileDo();
                        fileDo.setKeyWord(keyWord);
                        fileDo.setFileType(FileUtils.getFileType(filePath));
                        fileDo.setFilePath(filePath);
                        fileDo.setFileName(FileUtils.getFileName(filePath) + " ---- " + "工作簿（" + (i + 1) + "）" + worksheet.getName());
                        fileDo.setFileNameHtml(FileUtils.getFileName(filePath) + FileUtils.setHeightLight("工作簿（" + (i + 1) + "）" + worksheet.getName(), "success"));
                        LinkedHashSet<String> content = new LinkedHashSet<>();
                        LinkedHashSet<String> contentHtml = new LinkedHashSet<>();
                        for (CellRange cellRange : allString) {
                            String matchString = cellRange.getRichText().getText();
                            content.add(matchString);
                            contentHtml.add(FileUtils.setKeyWordHeightLight(matchString, keyWord, "danger"));
                        }
                        fileDo.setContent(content);
                        fileDo.setContentHtml(contentHtml);
                        returnDo.getFileDos().add(fileDo);
                        //文件数量新增
                        returnDo.setCountKeyWordsFile(returnDo.getCountKeyWordsFile()+1);
                        returnDo.setCountExcelFile(returnDo.getCountExcelFile()+1);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("处理excel异常：" + e.getMessage());
        }
    }

}
